package cn.xiaopengstack.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import cn.xiaopengstack.domain.award.aggregate.GiveOutCreditAwardAggregate;
import cn.xiaopengstack.domain.award.aggregate.UserAwardOrderAggregate;
import cn.xiaopengstack.domain.award.event.SendAwardMessageEvent;
import cn.xiaopengstack.domain.award.model.entity.AwardEntity;
import cn.xiaopengstack.domain.award.model.entity.UserAwardOrderEntity;
import cn.xiaopengstack.domain.award.model.entity.UserAwardRecordEntity;
import cn.xiaopengstack.domain.award.model.entity.UserCreditAwardEntity;
import cn.xiaopengstack.domain.award.model.valobj.AccountStatusVO;
import cn.xiaopengstack.domain.award.model.valobj.AwardStateVO;
import cn.xiaopengstack.domain.award.repository.IRaffleAwardRepository;
import cn.xiaopengstack.domain.task.model.TaskEntity;
import cn.xiaopengstack.infrastructure.event.EventPublisher;
import cn.xiaopengstack.infrastructure.persistent.dao.IAwardDao;
import cn.xiaopengstack.infrastructure.persistent.dao.ITaskDao;
import cn.xiaopengstack.infrastructure.persistent.dao.IUserAwardRecordDao;
import cn.xiaopengstack.infrastructure.persistent.dao.IUserCreditAccountDao;
import cn.xiaopengstack.infrastructure.persistent.po.Award;
import cn.xiaopengstack.infrastructure.persistent.po.Task;
import cn.xiaopengstack.infrastructure.persistent.po.UserAwardRecord;
import cn.xiaopengstack.infrastructure.persistent.po.UserCreditAccount;
import cn.xiaopengstack.infrastructure.persistent.redis.IRedisService;
import cn.xiaopengstack.types.enums.ResponseCode;
import cn.xiaopengstack.types.exception.AppException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * @author jiangyangang
 */
@Slf4j
@Repository
public class RaffleAwardRepository implements IRaffleAwardRepository {

    @Resource
    private IUserAwardRecordDao userAwardRecordDao;

    @Resource
    private ITaskDao taskDao;

    @Resource
    private IDBRouterStrategy dbRouter;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private EventPublisher eventPublisher;

    @Resource
    private IRedisService redisService;

    @Resource
    private IUserCreditAccountDao userCreditAccountDao;

    @Resource
    private IAwardDao awardDao;

    @Override
    public void createUserAwardOrder(UserAwardOrderAggregate orderAggregate) {
        UserAwardOrderEntity orderEntity = orderAggregate.getUserAwardOrderEntity();
        SendAwardMessageEvent<SendAwardMessageEvent.SendAwardMessage> event = orderAggregate.getSendAwardMessageEvent();
        TaskEntity taskEntity = orderAggregate.getTaskEntity();

        UserAwardRecord userAwardRecord = new UserAwardRecord();
        userAwardRecord.setAwardId(orderEntity.getAwardEntity().getAwardId());
        userAwardRecord.setAwardState(orderEntity.getAwardState().getCode());
        userAwardRecord.setAwardTime(orderEntity.getAwardTime());
        userAwardRecord.setAwardTitle(orderEntity.getAwardEntity().getAwardTitle());
        userAwardRecord.setUserId(orderEntity.getUser().getUserId());
        userAwardRecord.setOrderId(orderEntity.getOrderId());
        userAwardRecord.setActivityId(orderEntity.getActivityEntity().getActivityId());
        userAwardRecord.setCreateTime(new Date());
        userAwardRecord.setUpdateTime(new Date());
        userAwardRecord.setStrategyId(orderEntity.getActivityEntity().getStrategyId());

        Task task = new Task();
        task.setUserId(taskEntity.getUser().getUserId());
        task.setTopic(event.topic());
        task.setMessage(JSONObject.toJSONString(event.payload()));
        task.setState(taskEntity.getState().getCode());
        task.setMessageId(taskEntity.getMessageId());
        task.setCreateTime(new Date());

        try {
            dbRouter.doRouter(orderEntity.getUser().getUserId());

            transactionTemplate.execute(status -> {
                try {
                    userAwardRecordDao.insertSelective(userAwardRecord);

                    taskDao.insertSelective(task);
                    return 1;
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.AWARD_SAVE_ERROR.getCode(), ResponseCode.AWARD_SAVE_ERROR.getInfo());
                }
            });

        } finally {
            dbRouter.clear();
        }

        try {
            // 发送MQ
            eventPublisher.publish(event);
            taskDao.updateTaskSendMessageCompleted(task);
        } catch (Exception e) {
            taskDao.updateTaskSendMessageFail(task);
            log.error("写入中奖记录，发送MQ消息失败 userId: {} topic: {}", orderEntity.getUser().getUserId(), task.getTopic());
        }
    }

    @Override
    public int queryUserWinLotteryCount(String userId, Integer awardId) {
        UserAwardRecord userAwardRecord = new UserAwardRecord();
        userAwardRecord.setAwardId(awardId);
        userAwardRecord.setUserId(userId);
        return userAwardRecordDao.queryUserWinLotteryCount(userAwardRecord);
    }

    @Override
    public void saveGiveOutCreditAwardOrder(GiveOutCreditAwardAggregate giveOutAwardAggregate) {
        String userId = giveOutAwardAggregate.getUserId();
        UserCreditAwardEntity userCreditAwardEntity = giveOutAwardAggregate.getUserCreditAwardEntity();
        UserAwardRecordEntity userAwardRecordEntity = giveOutAwardAggregate.getUserAwardRecordEntity();


        UserAwardRecord userAwardRecord = new UserAwardRecord();
        userAwardRecord.setAwardState(AwardStateVO.complete.getCode());
        userAwardRecord.setUpdateTime(new Date());
        userAwardRecord.setOrderId(userAwardRecordEntity.getOrderId());
        userAwardRecord.setUserId(userId);

        UserCreditAccount userCreditAccount = new UserCreditAccount();
        userCreditAccount.setUserId(userId);
        userCreditAccount.setUpdateTime(new Date());
        userCreditAccount.setCreateTime(new Date());
        userCreditAccount.setAvailableAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccount.setTotalAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccount.setAccountStatus(AccountStatusVO.open.getCode());
        try {
            dbRouter.doRouter(userId);

            transactionTemplate.execute(status -> {
                try {
                    userAwardRecordDao.updateByComplete(userAwardRecord);
                    int result = userCreditAccountDao.updateCreditAmount(userId, userCreditAwardEntity.getCreditAmount());
                    if (result <= 0) {
                        userCreditAccountDao.insertSelective(userCreditAccount);
                    }
                    return 1;
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.AWARD_SAVE_ERROR.getCode(), ResponseCode.AWARD_SAVE_ERROR.getInfo());
                }
            });

        } finally {
            dbRouter.clear();
        }

    }

    @Override
    public Optional<UserAwardRecordEntity> queryAwardRecordByOrderId(String userId, String orderId) {
        try {
            dbRouter.doRouter(userId);
            UserAwardRecord userAwardRecord = userAwardRecordDao.queryAwardRecordByOrderId(userId, orderId);
            if (Objects.isNull(userAwardRecord)) {
                return Optional.empty();
            }

            UserAwardRecordEntity userAwardRecordEntity = new UserAwardRecordEntity();
            BeanUtils.copyProperties(userAwardRecord, userAwardRecordEntity);

            return Optional.of(userAwardRecordEntity);
        } finally {
            dbRouter.clear();
        }
    }

    @Override
    public Optional<AwardEntity> queryAwardEntity(Integer awardId) {
        Award award = awardDao.queryById(awardId);
        if (Objects.isNull(award)) {
            return Optional.empty();
        }
        AwardEntity awardEntity = new AwardEntity();
        BeanUtils.copyProperties(award, awardEntity);
        return Optional.of(awardEntity);
    }
}
